package com.robot.service;

import com.alibaba.fastjson.JSONArray;
import com.google.common.base.Splitter;
import com.robot.dao.InterfaceMapper;
import com.robot.model.Protocol;
import com.robot.util.DubboParamTypeInit;
import com.robot.util.TelnetSocket;
import com.robot.vo.ResponseMessage;
import lombok.extern.slf4j.Slf4j;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNoNodeException;
import org.I0Itec.zkclient.exception.ZkTimeoutException;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created with IntelliJ IDEA.
 * User: li
 * Date: 2019/9/8
 * Time: 15:07
 * PROJECT：robot
 * PACKAGE: com.robot
 *
 * @author:55324936@qq.com To change this template use File | Settings | File Templates.
 */
@Service
@Slf4j
public class DubboService {
    static Pattern ipPattern = Pattern.compile("(?<=//).*(?=/)");
    static Pattern paramPattern = Pattern.compile("(?<=\\().*(?=\\))");
    static Map paramTemplates = new HashMap();
    @Autowired
    InterfaceMapper interfaceMapper;

    /**
     * 从paramTemplates静态map里面获取模板信息
     **/
    public ResponseMessage paramTemplates(String interfaceName, String ip, String interfaceMethod) {
        Object template = paramTemplates.get(interfaceName + ip + interfaceMethod);
        return ResponseMessage.successResponse(template);
    }

    /**
     * 获取当前的在注册中心的接口列表
     *
     * @param zkAddress
     **/
    public ResponseMessage interfaceList(String zkAddress) {
        ZkClient zkClient = null;
        try {
            zkClient = new ZkClient(zkAddress, 3000);
        } catch (ZkTimeoutException z) {
            log.error("连接zk超时", z);
            return ResponseMessage.errorResponse("注册中心连接异常,请确认地址是否正确");
        }
        List<String> stringList = null;
        try {
            stringList = zkClient.getChildren("/dubbo");
            return ResponseMessage.successResponse(stringList);
        } catch (ZkNoNodeException z) {
            log.error("节点未找到{}", z);
            return ResponseMessage.errorResponse("请确认节点是否存在");
        } catch (Exception e) {
            log.error("获取zk节点异常", e);
            return ResponseMessage.errorResponse("获取节点异常,请联系系统管理员");
        }
    }

    /**
     * 通过接口获取服务提供者的ip和端口
     *
     * @param zkAddress
     * @param interfaceName
     * @return ResponseMessage
     **/
    public ResponseMessage resolveIp(String zkAddress, String interfaceName) {
        ZkClient zkClient = null;
        try {
            zkClient = new ZkClient(zkAddress, 3000);
        } catch (ZkTimeoutException z) {
            log.error("连接zk超时", z);
            return ResponseMessage.errorResponse("注册中心连接异常,请确认地址是否正确");
        }
        List<String> stringList = null;
        try {
            stringList = zkClient.getChildren("/dubbo/" + interfaceName + "/providers");
        } catch (ZkNoNodeException z) {
            log.error("接口未找到{}", interfaceName, z);
            return ResponseMessage.errorResponse("请确认接口是否存在,接口:" + interfaceName);
        } catch (Exception e) {
            log.error("获取zk节点异常", e);
            return ResponseMessage.errorResponse("获取节点异常,请联系系统管理员");
        }
        List<String> ipList = Lists.newArrayList();
        stringList.forEach(s -> {
            String url = null;
            try {
                url = URLDecoder.decode(s, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.error("解析url异常,接口名:{}", interfaceName);
            }
            if (!StringUtils.isEmpty(url)) {
                Matcher matcher = ipPattern.matcher(url);
                if (matcher.find()) {
                    String ip = matcher.group();
                    //如果你回到公司用到的话，并不需要替换ip
                    ip = ip.replace("172.31.145.55", "47.105.55.243");
                    ipList.add(ip);
                }
            }
        });
        return ResponseMessage.successResponse(ipList);
    }

    /**
     * 用于解析方法
     *
     * @param ipPort
     * @param interfaceName
     **/
    public ResponseMessage resolveMethod(String ipPort, String interfaceName) {
        String[] ips = ipPort.split(":");
        String ip = ips[0];
        Integer port = Integer.parseInt(ips[1]);
        Map map = new HashMap();
        try {
            TelnetSocket telnetSocket = new TelnetSocket(ip, port);
            String method = telnetSocket.send("ls -l " + interfaceName);
            method = method.replace("dubbo>", "");
            String[] methods = method.split("\r\n");
            List<String> methodList = Lists.newArrayList();
            for (int i = 0; i < methods.length; i++) {
                String methodName = methods[i];
                methodName = methodName.substring(methodName.indexOf(" ")).trim();
                methodList.add(methodName);
                Matcher matcher = paramPattern.matcher(methodName);
                String paramType = null;
                if (matcher.find()) {
                    paramType = matcher.group();
                }
                if (StringUtils.isEmpty(paramType)) {
                    continue;
                }
                List<String> paramTypeList = new ArrayList<>();
                if (paramType.contains(",")) {
                    paramTypeList = Splitter.on(",").trimResults().splitToList(paramType);
                } else {
                    paramTypeList.add(paramType);
                }
                paramTypeList = DubboParamTypeInit.initParamTemplate(paramTypeList);
                if (i == 0) {
                    map.put("param", paramTypeList);
                }
                paramTemplates.put(interfaceName + ipPort + methodName, paramTypeList);
            }
            map.put("methodList", methodList);
            return ResponseMessage.successResponse(map);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ResponseMessage.successResponse();
    }

    /**
     * 执行dubbo调用
     **/
    public String invoke(Protocol protocol) {
        String[] ips = protocol.getHost().split(":");
        String ip = ips[0];
        Integer port = Integer.parseInt(ips[1]);
        try {
            TelnetSocket telnetSocket = new TelnetSocket(ip, port);
            telnetSocket.setReadEncoding(protocol.getEncoding());
            telnetSocket.setWriteEncoding(protocol.getEncoding());
//            invoke com.longteng.service.DubboTestService.sayHello("hi")
            String interfaceName = protocol.getInterfaceName();
            String interfaceMethod = protocol.getInterfaceMethod();
            String cmd;
//            如果是以()结尾的说明没有入参
            if (interfaceMethod.endsWith("()")) {
                cmd = "invoke " + interfaceName + "." + interfaceMethod;
            } else {
                String param = protocol.getParam();
                param=param.substring(1,param.length()-1);
                /**开头和结尾多了 [] **/
                String methodName =interfaceMethod.substring(0,interfaceMethod.indexOf("(")).trim();
                cmd = "invoke  " + interfaceName + "." + methodName+"("+param+")";
            }
            String result = telnetSocket.send(cmd);
            result=result.substring(0,result.indexOf("\r\n"));
            return result;
        } catch (Exception e) {
            log.error("请求dubbo接口异常",e);
        }
        return null;
    }
    /**
     * 执行测试用例里面的调用
     * **/
    public String invokeForTestCase(Protocol protocol) {
        Protocol protocolDb=  interfaceMapper.getProtocolById(protocol.getId());
        protocolDb.setParam(protocol.getParam());
        return this.invoke(protocolDb);
    }
    /**
     * 测试用例调用一次dubbo
     * @param param
     * @param interfaceId
     * **/
    public String invokeForTestPlan(Integer interfaceId,String param){
        Protocol protocol= interfaceMapper.getProtocolById(interfaceId);
        protocol.setParam(param);
        return this.invoke(protocol);
    }
}
